/**
 * Copyright 厦门中软海晟信息技术有限公司 版权所有 违者必究 2019
 */
package com.opencvjava.piano;

import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.springframework.stereotype.Service;

import javax.management.relation.RoleUnresolved;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import static com.opencvjava.support.util.CvUtils.imshow;
import static com.opencvjava.support.util.CvUtils.mat;
import static org.opencv.imgproc.Imgproc.COLOR_BGR2GRAY;
import static org.opencv.imgproc.Imgproc.THRESH_BINARY;
import static org.opencv.imgproc.Imgproc.THRESH_BINARY_INV;
import static org.opencv.imgproc.Imgproc.THRESH_TRIANGLE;
import static org.opencv.imgproc.Imgproc.rectangle;
import static org.opencv.imgproc.Imgproc.resize;
import static org.opencv.imgproc.Imgproc.threshold;

/**
 * @author : sunzb
 * @date: 2019/11/21
 */
@Service
public class Extract2 {
    private Random random = new Random();
    private int rows, cols, total, lineLength, rowIndex, index;
    private byte[] data;
    private byte[] dataNoLine;
    private byte[] dataLine;
    private int sizeThreshold = 10;

    public void test() {
//        Mat gray = mat("piano", "1.jpg", Imgcodecs.IMREAD_GRAYSCALE);
        Mat gray = mat("piano", "test.jpg", Imgcodecs.IMREAD_GRAYSCALE);
        if (gray.empty()) throw new RuntimeException("源图读取失败");
        rows = gray.rows();
        cols = gray.cols();
        lineLength = cols / 2;
        total = (int) gray.total();
        Imgcodecs.imwrite("I:\\project\\cv\\opencv-java\\src\\main\\resources\\images\\piano\\grayTest.jpg", gray);
        data = new byte[total];
        gray.get(0, 0, data);
        /*byte[] newData = new byte[total];
        for (int i = 0; i < total; i++) newData[i] = data[i];
        int kernalR = 2;
        for (int row = kernalR; row < rows - kernalR; row++) {
            for (int col = kernalR; col < cols - kernalR; col++) {
                int color = data[row * cols + col];
                int sum = 0, avg = 0;
                for (int curRow = - kernalR; curRow <= kernalR; curRow++) {
                    for (int curCol = -kernalR; curCol <= kernalR; curCol++) {
                        index = (row + curRow) * cols + (col + curCol);
                        sum += data[index] & 0xff;
                    }
                }
                if (color < sum / 25) newData[row * cols + col] = 0;
                else newData[row * cols + col] = -1;
            }
        }
        Mat bin = new Mat(gray.size(), gray.type());
        bin.put(0, 0, newData);
        imshow("二值化", bin);
        List<Integer> lineRows = new ArrayList<>();
        dataNoLine = new byte[total];
        dataLine = new byte[total];
        int dataNoLineRow = 0, dataNoLineRowIndex, dataLineRow = 0, dataLineRowIndex;
        for (int row = 0; row < rows; row++) {
            dataNoLineRowIndex = dataNoLineRow * cols;
            dataLineRowIndex = dataLineRow * cols;
            rowIndex = row * cols;
            if (isLine(rowIndex)) {
                lineRows.add(dataLineRow);
                for (int col = 0; col < cols; col++) dataLine[dataLineRowIndex + col] = 100;
                dataLineRow++;
                for (int row1 = row + 1; row1 < rows; row1++) {
                    if (!isLine(row1 * cols)) {
                        row = row1;
                        break;
                    }
                }
            } else {
                for (int col = 0; col < cols; col++) dataNoLine[dataNoLineRowIndex + col] = data[rowIndex + col];
                for (int col = 0; col < cols; col++) dataLine[dataLineRowIndex + col] = data[rowIndex + col];
                dataNoLineRow++;
                dataLineRow++;
            }
        }
        System.out.println("lines size:" + lineRows.size());
//        if (lineRows.size() == 0) throw new RuntimeException("无法定位谱线");
//        if (lineRows.size() % 5 != 0) throw new RuntimeException("谱线不是5的倍数");
        Mat matNoLine = new Mat(bin.size(), bin.type());
        matNoLine.put(0, 0, dataNoLine);
        imshow("去谱线", matNoLine);
        Mat matLine = new Mat(bin.size(), bin.type());
        matLine.put(0, 0, dataLine);
        imshow("谱线细化分离", matLine);*/
        /*Mat bin = new Mat();
        threshold(gray, bin, 70, 255, THRESH_BINARY_INV | THRESH_TRIANGLE);
        imshow("三角二值化结果", bin);*/
        /*data = new byte[total];
        bin.get(0, 0, data);
        List<Integer> lineRows = new ArrayList<>();
        dataNoLine = new byte[total];
        dataLine = new byte[total];
        int dataNoLineRow = 0, dataNoLineRowIndex, dataLineRow = 0, dataLineRowIndex;
        for (int row = 0; row < rows; row++) {
            dataNoLineRowIndex = dataNoLineRow * cols;
            dataLineRowIndex = dataLineRow * cols;
            rowIndex = row * cols;
            if (isLine(rowIndex)) {
                lineRows.add(dataLineRow);
                for (int col = 0; col < cols; col++) dataLine[dataLineRowIndex + col] = 100;
                dataLineRow++;
                for (int row1 = row + 1; row1 < rows; row1++) {
                    if (!isLine(row1 * cols)) {
                        row = row1;
                        break;
                    }
                }
            } else {
                for (int col = 0; col < cols; col++) dataNoLine[dataNoLineRowIndex + col] = data[rowIndex + col];
                for (int col = 0; col < cols; col++) dataLine[dataLineRowIndex + col] = data[rowIndex + col];
                dataNoLineRow++;
                dataLineRow++;
            }
        }
        System.out.println("lines size:" + lineRows.size());
        if (lineRows.size() == 0) throw new RuntimeException("无法定位谱线");
        if (lineRows.size() % 5 != 0) throw new RuntimeException("谱线不是5的倍数");
        Mat matNoLine = new Mat(bin.size(), bin.type());
        matNoLine.put(0, 0, dataNoLine);
        imshow("去谱线", matNoLine);
        Mat matLine = new Mat(bin.size(), bin.type());
        matLine.put(0, 0, dataLine);
        imshow("谱线细化分离", matLine);
        int rowStart, rowEnd, count = 0, lineVerticalCount = 0;
        List<List<List<Integer>>> segments = new ArrayList<>();
        for (int i = 0; i < lineRows.size(); i = i + 10) {
            List<List<Integer>> blocks = new ArrayList<>();
            for (int col = 0; col < cols; col++) {
                rowStart = lineRows.get(i) - i + 1;
                rowEnd = lineRows.get(i + 9) - (i + 10);
                for (int row = rowStart; row < rowEnd; row++) {
                    if (dataNoLine[row * cols + col] == -1) {
                        ++count;
                        List<Integer> block = bfs(row, col);
                        if (block.size() > sizeThreshold) {
                            blocks.add(block);
                        }
                    }
                }
            }
            segments.add(blocks);
        }
        *//*for (int i = 0; i < lineRows.size(); i = i + 10) {
            rowStart = lineRows.get(i) - i + 1;
            rowEnd = lineRows.get(i + 9) - (i + 10);
            List<List<Integer>> blocks = new ArrayList<>();
            for (int row = rowStart; row < rowEnd; row++) {
                rowIndex = row * cols;
                for (int col = 0; col < cols; col++) if (dataNoLine[rowIndex + col] == -1) blocks.add(bfs(row, col));
            }
            segments.add(blocks);
            *//**//*List<List<Integer>> segment = new ArrayList<>();
            for (List<Integer> block : blocks) {
                if (isLineVertical(block)) {
                    segments.add(segment);
                    segment = new ArrayList<>();
                    continue;
                } else {
                    segment.add(block);
                }
            }*//**//*
        }*//*
        Mat matBlocks = new Mat(bin.size(), 16);
        byte[] dataBlocks = new byte[total * 3];
        int b, g, r, binRow, binCol;
        for (List<List<Integer>> segment : segments) {
            for (List<Integer> block : segment) {
                b = random.nextInt(256);
                g = random.nextInt(256);
                r = random.nextInt(256);
                for (int index : block) {
                    binRow = index / cols;
                    binCol = index % cols;
                    index = binRow * cols * 3 + binCol * 3;
                    dataBlocks[index] = (byte) (b & 0xff);
                    dataBlocks[index + 1] = (byte) (g & 0xff);
                    dataBlocks[index + 2] = (byte) (r & 0xff);
                }
            }
        }
        matBlocks.put(0, 0, dataBlocks);
        imshow("blocks", matBlocks);
        for (int i = 0; i < dataBlocks.length; i++) dataBlocks[i] = 0;
        for (List<List<Integer>> segment : segments) {
            b = random.nextInt(256);
            g = random.nextInt(256);
            r = random.nextInt(256);
            for (List<Integer> block : segment) {
                for (int index : block) {
                    binCol = index % cols;
                    binRow = index / cols;
                    index = binRow * cols * 3 + binCol * 3;
                    dataBlocks[index] = (byte) (b & 0xff);
                    dataBlocks[index + 1] = (byte) (g & 0xff);
                    dataBlocks[index + 2] = (byte) (r & 0xff);
                }
            }
        }
        matBlocks.put(0, 0, dataBlocks);
        imshow("segment blocks", matBlocks);*/
    }

    private boolean isLine(int rowIndex) {
        int count = 0;
        for (int col = 0; col < cols; col++) if (data[rowIndex + col] == -1) count++;
        return count > lineLength;
    }

    private List<Integer> bfs(int row, int col) {
        List<Integer> block = new ArrayList<>();
        LinkedList<Pixel> bfs = new LinkedList<>();
        bfs.push(new Pixel().setCol(col).setRow(row));
        while (!bfs.isEmpty()) {
            Pixel pop = bfs.pop();
            if (pop.getRow() < 0 || pop.getRow() >= rows) continue;
            if (pop.getCol() < 0 || pop.getCol() >= cols) continue;
            int index = pop.getRow() * cols + pop.getCol();
            int color = dataNoLine[index];
            if (color == 0) continue;
            block.add(index);
            dataNoLine[index] = 0;
            bfs.push(new Pixel().setRow(pop.getRow()).setCol(pop.getCol() - 1));
            bfs.push(new Pixel().setRow(pop.getRow()).setCol(pop.getCol() + 1));
            bfs.push(new Pixel().setRow(pop.getRow() - 1).setCol(pop.getCol()));
            bfs.push(new Pixel().setRow(pop.getRow() + 1).setCol(pop.getCol()));
        }
        return block;
    }

    private class Pixel {
        int row;
        int col;

        public int getRow() {
            return row;
        }

        public Pixel setRow(int row) {
            this.row = row;
            return this;
        }

        public int getCol() {
            return col;
        }

        public Pixel setCol(int col) {
            this.col = col;
            return this;
        }
    }
}
